#include "mode.h"
#include "main.h"

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "fatfs.h"
// #include "libjpeg.h"
#include "usb_device.h"

// #include "../lv_lib_png/lodepng.h"
#include <limits.h>

#define MAX_BMP_FILES 32	  // 32
#define MAX_IMAGE_FILE_LEN 32 // 32
#define DEFAULT_IMAGE_CHANGE_INTERVAL 800
// #define blockSize 8
#define linecount 30 // 20
#define GRamSize (linecount * LCD_Width * 3)
__attribute__((section(".ccmram"))) uint8_t lineData[GRamSize] = {0};

FRESULT res;
struct RA8876_Pixel_Mode2
{
	uint8_t b;
	uint8_t g;
	uint8_t r;
} __attribute__((packed));

typedef struct __attribute__((packed)) tagBITMAPFILEHEADER
{
	uint16_t bFileType;
	uint32_t bFileSize;
	uint32_t bReserved1;
	// uint16_t bReserved2;
	uint32_t bPixelDataOffset;
} BITMAPFILEHEADER; // 14bytes
/* __attribute__((packed))，它的作用就是告诉编译器：
取消结构体在编译过程中的优化对齐，按尽可能小的size对齐——也就是按1字节为单位对齐。*/

// typedef struct tagBITMAPFILEHEADER BITMAPFILEHEADER;

typedef struct __attribute__((packed)) tagBITMAPINFOHEADER
{
	uint32_t bHeaderSize;	   // 图像信息头总大小（40bytes）
	uint32_t bImageWidth;	   // 图像宽度（像素）
	uint32_t bImageHeight;	   // 图像高度
	uint16_t bPlanes;		   // 应该是0
	uint16_t bBitsPerPixel;	   // 像素位数
	uint32_t bCompression;	   // 图像压缩方法
	uint32_t bImageSize;	   // 图像大小（字节）
	uint32_t bXpixelsPerMeter; // 横向每米像素数
	uint32_t bYpixelsPerMeter; // 纵向每米像素数
	uint32_t bTotalColors;	   // 使用的颜色总数，如果像素位数大于8，则该字段没有意义
	uint32_t bImportantColors; // 重要颜色数，一般没什么用
} BITMAPINFOHEADER;			   // 40byte

// typedef struct tagBITMAPINFOHEADER BITMAPINFOHEADER;

extern SD_HandleTypeDef hsd;
extern UART_HandleTypeDef huart1;
// extern SDRAM_HandleTypeDef hsram1;

const char Title[] = {" \r\n\
 _____            _     _   __\r\n\
(_   _)          ( )_ /  )  _  \\\r\n\
  | |    __   ___|  _)_  | ( ) |\r\n\
  | |  / __ \\  __) |   | | | | |\r\n\
  | | (  ___/__  \\ |_  | | (_) |\r\n\
  (_)  \\____)____/\\__) (_)\\___/\r\n\
   \r\n\
Copyright (c) zhaitao.as@outlook.com\r\n\
|||||||||||||||||||||||||||||||||||||\r\n\
\r\n\
"};

uint32_t testInterval = DEFAULT_IMAGE_CHANGE_INTERVAL;
FATFS fs;
FIL fil;
FRESULT fr;
uint32_t br;
uint8_t imageFileCount = 0;
char imageFileNames[MAX_BMP_FILES][MAX_IMAGE_FILE_LEN];
char *imageFileSorted[MAX_BMP_FILES];
// static uint8_t lineData[2048 * 3 * blockSize];

uint8_t rd;
uint8_t getUartFlag = 0;

/////////////////
uint32_t file_byte = 0;	   // 文件大小（字节数）
uint32_t byteswritten = 0; // 写文件计数
uint32_t bytesread = 0;	   // 读文件计数

uint8_t wtext[] = "THAT IS ME 4G CARD"; // 写的内容
uint8_t rtext[1024];					// 读取的buff,1024bytes

char filename[] = "log.txt"; // 文件名
extern uint8_t retSD;		 /* Return value for SD */
///////////////////

int mipiCheckIDDcs(void);
int mipiCheckIDGen(void);

/**
 * @brief get image (bmp, jpg) file name from sdcard
 *
 * @param path folder path
 * @param ifn image file names buffer
 * @return int file number
 */
int getSDCardImageFileNames(char *path, char *ifn[])
{
	FILINFO info;
	DIR file_dir;
	FRESULT fr;
	int cnt = 0;

	fr = f_opendir(&file_dir, path);
	if (fr)
	{
		printf("FATFS f_opendir ERROR\r\n");
		Error_Handler();
	}

	while (1)
	{
		fr = f_readdir(&file_dir, &info);

		if (fr || info.fname[0] == 0)
			break;
		if (info.fname[0] == '.')
			continue;
		if (strstr(info.fname, ".bmp") || strstr(info.fname, ".BMP"))
		{

			// only get file name in number
			char fileName[32];
			strcpy(fileName, info.fname);
			// printf("fileName ==== %s \r\n",fileName);
			char *pch;
			pch = strtok(fileName, ".");
			if (atoi(pch))
			{
				strcpy(ifn[cnt], info.fname);
				cnt++;
			}
		}
	}

	return cnt;
}

void SDCardCheck(void)
{
	int result = 0;

	//
	res = f_mount(&fs, "", 0);
	if (res)
	{
		printf("SD Check f_mount error %d\r\n", res);
		result += 1;
	}

	//
	res = f_open(&fil, "FatFs.txt", FA_OPEN_ALWAYS | FA_WRITE);
	if (res)
	{
		printf("SD Check1 f_open error %d\r\n", res);
		result += 1;
	}

	//
	uint8_t wtext[] = "SD Check: Read from write file SUCCESS\r\n";
	uint8_t rtext[100];
	uint32_t byteswritten;
	uint32_t bytesread;
	res = f_write(&fil, wtext, sizeof(wtext), (void *)&byteswritten);
	if (res)
	{
		printf("SD Check f_write error %d\r\n", res);
		result += 1;
	}

	//
	res = f_close(&fil);
	if (res)
	{
		printf("SD Check f_close error %d\r\n", res);
		result += 1;
	}

	//
	res = f_open(&fil, "FatFs.txt", FA_READ | FA_OPEN_EXISTING);
	if (res != FR_OK)
	{
		printf("SD Check2 f_open error %d\r\n", res);
		result += 1;
	}

	//
	res = f_read(&fil, rtext, 54, (UINT *)&bytesread);
	if (res != FR_OK)
	{
		printf("SD Check read error %d\r\n", res);
		result += 1;
	}

	//
	res = f_close(&fil);
	if (res)
	{
		printf("SD Check f_close error %d\r\n", res);
		result += 1;
	}

	// remove testing file
	res = f_unlink("Fatfs.txt");
	if (res)
	{
		printf("SD Check f_unlink error %d\r\n", res);
		result += 1;
	}

	if (result != 0)
	{
		printf("SDcard check failed\r\n");
		printf("SD Check write %d bytes, read %d bytes\r\n",
			   (int)byteswritten, (int)bytesread);
		printf((const char *)rtext);

		Error_Handler();
	}
	else
	{
		printf("[SDcard] Check Success\r\n");
	}

	return;
}

uint32_t getBmpRawOffset(uint8_t *BMP_Header)
{
	/*uint32_t fileSize = 0;
	fileSize = ((uint32_t)(BMP_Header[5]) << 24) +
		   ((uint32_t)(BMP_Header[4]) << 16) +
		   ((uint32_t)(BMP_Header[3]) << 8) +
		   ((uint32_t)(BMP_Header[2]) << 0);
	// printf("filesize: %d\r\n", fileSize);

	uint32_t rawDataSize = 0;
	rawDataSize = ((uint32_t)(BMP_Header[37]) << 24) +
			  ((uint32_t)(BMP_Header[36]) << 16) +
			  ((uint32_t)(BMP_Header[35]) << 8) +
			  ((uint32_t)(BMP_Header[34]) << 0);
	// printf("rawDataSize: %d\r\n", rawDataSize);

	return fileSize - rawDataSize;*/
	return BMP_Header[2];
}

uint8_t bmpDrawFile(char *fileName)
{

	int __error = 0;
	// static FIL fil;
	uint8_t BMP_Header[54];
	uint16_t width, height;
	volatile uint16_t x;
	volatile struct RA8876_Pixel_Mode2 *pixel;

	printf("bmpDrawFile: %s \r\n", fileName);

	res = f_open(&fil, (const TCHAR *)fileName, FA_READ | FA_OPEN_EXISTING);
	if (res != FR_OK)
	{
		printf("bmpDrawFile f_open error %d\r\n", res);
		__error = 2;
		goto bmpDrawFile_close;
	}

	//
	res = f_read(&fil, BMP_Header, 54, (UINT *)&br);
	if (res != FR_OK)
	{
		printf("bmpDrawFile file read error\r\n");
		__error = 3;
		goto bmpDrawFile_close;
	}

	BITMAPFILEHEADER *fh = (BITMAPFILEHEADER *)BMP_Header;		  // 前14字节
	BITMAPINFOHEADER *ih = (BITMAPINFOHEADER *)(BMP_Header + 14); // 54-14=40字节

	if (fh->bFileType != 0x4D42)
	{
		printf("bmpDrawFile file type should 0x4D42, get: %04x\r\n", fh->bFileType);
		__error = 1;
		goto bmpDrawFile_close;
	}

	width = ih->bImageWidth;
	height = ih->bImageHeight;
	uint32_t lineDataSize = width * 3; /* width * 3, In Byte */

	if (lineDataSize % 4)
	{ // BMP格式，170*3除4，有余数，自动补了两个字节；
		lineDataSize += 4 - lineDataSize % 4;
	}
	uint32_t filesize = fh->bPixelDataOffset + lineDataSize * height;
	uint32_t lineCountGRamCache = GRamSize / lineDataSize;

	static uint32_t lineStartPosition = 0;
	static uint32_t br = 0;
	uint32_t lineDrawed = 0;
	uint32_t lineToCache = 0;
	uint32_t dataDrawed = 0;
	uint32_t dataToDraw = 0;
	wr_cmd_parall1_8(0x2A);
	wr_dat_parall1_8(0x00);
	wr_dat_parall1_8(0x00);
	wr_dat_parall1_8(0x00);
	wr_dat_parall1_8(0xEF);

	wr_cmd_parall1_8(0x2B);
	wr_dat_parall1_8(0x00);
	wr_dat_parall1_8(0x00);
	wr_dat_parall1_8(0x01);
	wr_dat_parall1_8(0x3F);

	wr_cmd_parall1_8(0x2C);
	while (lineDrawed < height)
	{

		/* Calculate cache size */
		if ((lineDrawed + lineCountGRamCache) < height)
		{
			// 没到图片结尾, 缓存gram所能容纳的最大行数
			lineToCache = lineCountGRamCache;
		}
		else
		{
			// 到了结尾, 只需要缓存一部分行
			lineToCache = height - lineDrawed;
		}

		/* 	Calculate new cache area start position
			bmp图片是y翻转存放的, 文件头之后是图片最后一行, 文件结尾是图片的第一行
			为了保准观感上刷图是从屏的0行开始, 要先从文件结尾部分开始刷
			先找到文件结尾, 再减去已经刷过的行, 再减去这一次要刷的行, 就是新的起点
		*/
		dataDrawed = lineDrawed * lineDataSize;
		dataToDraw = lineToCache * lineDataSize;
		br = filesize - dataDrawed - dataToDraw;

		/* Move Pointer to position of new cache */
		if (f_lseek(&fil, br))
		{
			printf("bmpDrawFile f_lseek error\r\n");
			__error = 3;
			goto bmpDrawFile_close;
		}

		/* Read Line Data */
		if (f_read(&fil, lineData, dataToDraw, (UINT *)&br))
		{
			printf("bmpDrawFile f_read error\r\n");
			__error = 4;
			goto bmpDrawFile_close;
		}
		/* Draw cached data */
		// uint8_t color1,color2,color3;
		for (uint32_t i = 0; i < lineToCache; i++)
		{
			lineStartPosition = (lineToCache - 1 - i) * lineDataSize;
			pixel = (struct RA8876_Pixel_Mode2 *)(lineData + lineStartPosition);

/* Draw one line of cached*/
#if (SPI_565)
			{
				for (x = 0; x < width; x++)
				{
					// uint8_t color1 = (pixel->r & 0xf8) | ((pixel->g & 0xfc) >> 5); // 16bit
					// uint8_t color2 = ((pixel->g & 0xfc) << 3) | ((pixel->b & 0xf8) >> 3);
					//  printf("pixel->r = %ld,pixel->g = %ld,pixel->b = %ld\n\r",pixel->r,pixel->g,pixel->b);
					uint8_t color1 = pixel->r;
					uint8_t color2 = pixel->g;
					uint8_t color3 = pixel->b;
					wr_dat_4spi_8bcs(color1);
					wr_dat_4spi_8bcs(color2);
					wr_dat_4spi_8bcs(color3);
					pixel++;
				}
			}
#endif
#if (Parallel1_16bit)
			{
				for (x = 0; x < width; x += 2) // 每次处理2个像素（3次16位写入）
				{
					// 提取第一个像素的RGB（各6位）
					uint8_t r1 = (pixel->r >> 2) & 0x3F; // R5-R0
					uint8_t g1 = (pixel->g >> 2) & 0x3F; // G5-G0
					uint8_t b1 = (pixel->b >> 2) & 0x3F; // B5-B0
					pixel++;							 // 移动到下一个像素

					// 提取第二个像素的RGB（各6位）
					uint8_t r2 = (pixel->r >> 2) & 0x3F;
					uint8_t g2 = (pixel->g >> 2) & 0x3F;
					uint8_t b2 = (pixel->b >> 2) & 0x3F;
					pixel++; // 移动到下一个像素（如果还有）

					// 构造3个16位写入：
					// 1. 第一个像素的R + G（各6位，后面补2个0）
					uint16_t write1 = (r1 << 10) | (g1 << 2);

					// 2. 第一个像素的B + 第二个像素的R
					uint16_t write2 = (b1 << 10) | (r2 << 2);

					// 3. 第二个像素的G + B
					uint16_t write3 = (g2 << 10) | (b2 << 2);

					// 发送3次16位数据
					wr_dat_parall1_16(write1);
					wr_dat_parall1_16(write2);
					wr_dat_parall1_16(write3);
				}
			}
#endif
#if (Parallel2_18bit)
			{
				for (x = 0; x < width; x++)
				{
					uint16_t color_H = (pixel->r & 0xc0) >> 6; // 18bit-取高位
					// uint16_t color_L = ((pixel->r & 0x3c) <<10) | ((pixel->g & 0xfc)<<4) | ((pixel->b & 0xfc)>>2);
					uint16_t color_L =
						(((pixel->r & 0x3c) << 10) & 0b1111000000000000) |
						(((pixel->g & 0xfc) << 04) & 0b0000111111000000) |
						(((pixel->b & 0xfc) >> 02) & 0b0000000000111111);
					wr_displ_parall2_18(color_H, color_L);
					pixel++;
				}
			}
#endif
#if (Parallel1_8bit)
			{
				for (x = 0; x < width; x++)
				{
					wr_displ_parall1_8(pixel->r);
					wr_displ_parall1_8(pixel->g);
					wr_displ_parall1_8(pixel->b);
					pixel++;
				}
			}
#endif
		}
		////HAL_GPIO_WritePin(ULED2_GPIO_Port, ULED2_Pin, GPIO_PIN_SET);

		lineDrawed += lineToCache;
	}

bmpDrawFile_close:

	if (f_close(&fil))
	{
		printf("bmpDrawFile f_close error\r\n");
	}

	return __error;
}
uint8_t drawFile(char *fn)
{
	uint8_t r = 0;

	// HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);

	if (strstr(fn, ".bmp") || strstr(fn, ".BMP"))
	{
		bmpDrawFile(fn);
	}
	else if (strstr(fn, ".jpg"))
	{
		// jpgDrawFile(fn);
	}
	else
	{
		r = -1;
	}

	// HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);

	return r;
}

/**
 * @retval None
 */

/************************************************************************************************
 *
 *  								Functions Export to Lua
 *
 ***********************************************************************************************/
/* @brief sort string
 *
 * @param strings
 * @param num
 */
void sortStrings(char *strings[], int num)
{
	char *temp;
	int top, seek;

	for (top = 0; top < num - 1; top++)
	{
		for (seek = top + 1; seek < num; seek++)
		{
			// if (strcmp(strings[top],strings[seek]) > 0)
			if (atoi(strings[top]) - atoi(strings[seek]) > 0)
			{
				temp = strings[top];
				strings[top] = strings[seek];
				strings[seek] = temp;
			}
		}
	}
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	UNUSED(huart);
	// HAL_UART_Transmit(&huart1, &rd, 1, 0xfff);
	getUartFlag = 1;
	// HAL_UART_Receive_IT(&huart1, &rd, 1);
}

int testModeStart(void)
{
	int result = 0;
	SDCardCheck(); ////Check sdcard

	// Get sdcard file names and sort
	do
	{
		for (int c = 0; c < MAX_BMP_FILES; c++)
		{
			imageFileSorted[c] = imageFileNames[c];
		}
		imageFileCount = getSDCardImageFileNames("", imageFileSorted);

		sortStrings(imageFileSorted, imageFileCount);

		printf("[LCMTEST] Sorted Image Files:\r\n");
		for (uint8_t i = 0; i < imageFileCount; i++)
		{
			printf("\t%s ", imageFileSorted[i]);
			if ((i > 0) && (i % 8 == 0))
			{
				printf("\r\n");
			}
		}
	} while (0);

	// Main Loop With Key Pause
	uint8_t pauseFlag = 0;
	uint32_t timePiece = testInterval + 1;
	uint8_t imageIndex = 0;
	////HAL_UART_Receive_IT(&huart1, &rd, 1);
	while (1)
	{
		// Detect Key State, and Change pauseFlag
		if (HAL_GPIO_ReadPin(PAUSE_PA5_GPIO_Port, PAUSE_PA5_Pin) == GPIO_PIN_RESET)
		{
			HAL_Delay(20);
			while (HAL_GPIO_ReadPin(PAUSE_PA5_GPIO_Port, PAUSE_PA5_Pin) == GPIO_PIN_RESET)
			{
			};
			// printf("HAS COUNT \r\n");
			if (pauseFlag == 0)
			{
				pauseFlag = 1;
				timePiece = 0;
				// HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
			}
			else
			{
				pauseFlag = 0;
				// HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
				timePiece = testInterval + 1; // change image immediately
			}
		}
		// Detect pause flag, if flag, timePiece++, else keep remain
		if (pauseFlag == 0)
		{
			timePiece++;
		}

		// Detect timePiece, if timePiece >= 1000, change display image,and make timePiece 0//
		if (timePiece >= testInterval)
		{
			timePiece = 0;

			if (imageIndex >= imageFileCount)
			{
				imageIndex = 0;
			}
			// printf("imageIndex  == %d ,testInterval  == %d \n\r ",imageIndex,testInterval);
			drawFile(imageFileSorted[imageIndex]);
			imageIndex++;
		}

		// time piece interval = 1ms
		HAL_Delay(1);
	}
	return result;
}
